package com.zc.im.seqsvr.business.impl;

import cn.hutool.core.collection.CollUtil;
import com.zc.im.common.constant.RedisPrefixConstant;
import com.zc.im.common.constant.StepConstant;
import com.zc.im.common.constant.enums.ResultEnum;
import com.zc.im.common.exception.CustomException;
import com.zc.im.seqsvr.business.SeqBusiness;
import com.zc.im.seqsvr.domain.MaxSeq;
import com.zc.im.seqsvr.domain.UserSeq;
import com.zc.im.seqsvr.script.LuaScript;
import com.zc.im.seqsvr.service.MaxSeqService;
import com.zc.im.seqsvr.service.UserSeqService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.ReactiveStringRedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import reactor.core.publisher.Mono;

/**
 * Project：im
 * Date：2022/4/18
 * Time：17:20
 * Description：业务层实现
 *
 * @author Challen.Zhang
 * @version 1.0
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class SeqBusinessImpl implements SeqBusiness {

    private final ReactiveStringRedisTemplate redisTemplate;

    private final UserSeqService userSeqService;

    private final MaxSeqService maxSeqService;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Mono<Long> getByUserId(Long userId) {
        return redisTemplate.execute(LuaScript.getIncrementUserSeq(), CollUtil.list(false,
                RedisPrefixConstant.CURRENT_SEQ + userId,
                RedisPrefixConstant.MAX_SEQ + (userId / 10),
                StepConstant.getStepSize().toString()))
                .next()
                .flatMap(x -> {
                    //说明maxSeq需要新增
                    if (x < 0) {
                        //return Mono.just(0 - x);
                        return maxSeqService.addStepSize(userId / 10, StepConstant.getStepSize())
                                .flatMap(rows -> {
                                    if (rows != 1L) {
                                        return Mono.error(new CustomException(ResultEnum.INTERNAL_SERVER_ERROR, "数据库异常"));
                                    }
                                    return userSeqService.updateSeq(userId, -x).flatMap(updateRows -> {
                                        if (updateRows != 1L) {
                                            return Mono.error(new CustomException(ResultEnum.INTERNAL_SERVER_ERROR, "数据库异常"));
                                        }
                                        return Mono.just(-x);
                                    });
                                });
                    } else {
                        return Mono.just(x);
                    }
                });
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Mono<Boolean> createByUserId(Long userId) {
        return maxSeqService.findById(userId / 10L)
                .defaultIfEmpty(new MaxSeq.Builder().id(userId / 10L).maxSeq(-1L).build())
                .flatMap(x -> {
                    if (x.getMaxSeq() == -1L) {
                        x.setMaxSeq(StepConstant.getStepSize());
                        return maxSeqService.save(x)
                                .flatMap(record ->
                                        redisTemplate.opsForValue().increment(RedisPrefixConstant.MAX_SEQ + record.getId(), StepConstant.getStepSize())
                                );
                    }
                    return Mono.just(x.getMaxSeq());
                }).flatMap(t -> userSeqService.save(new UserSeq.Builder().id(null).currentSeq(0L).build())
                        .flatMap(userSeq -> redisTemplate.opsForValue().set(RedisPrefixConstant.CURRENT_SEQ + userSeq.getId(), "0"))
                );
    }
}
